/*
 ============================================================================
 Name        : tp1.c
 Author      : 84236   90110   90131
 Version     :
 Copyright   : 
 Description : C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "DefinicionIndicesErrores.h"
#include "odump.h"
#include "my_string_library.h"
#define VERSION "1.0"

/*
 * Pre: Se respeta la siguiente convencion de argumentos:
 *
 * Usage:

 tp0 -h\n	tp0 -V\n	tp0 [options]

 Options:
	-h, --help	Print this information.
	-V, --version	Print version and quit.
	-O, --octal	Octal dump mode: encode the input (binary file) in octal
								format (default).
	-b, --binary	Binary mode: decode the input (octal dump file) in binary
								format.
	-i, --input	Path to input file (defaults to stdin).
	-o, --output	Path to output file (defaults to stdout).
	-l, --length	Number of octal bytes per line (line length) at the
					output in octal dump mode (0 = default = no limit).
	-d, --delimiter	Delimiter between octal bytes. Could be a string, not only
					a single byte. Used as delimiter for octal output and input\n");
					files (default: none).
	Examples:
		tp0\n	tp0 -i -
		tp0 -i - -o - -O
		tp0 -b
		tp0 -i /dev/random -o /tmp/random-octaldump -l 12

 * Post: Se realiza el volcado de acuerdo a los argumentos.
 */
int volcado(int argc , char *argv[]);

/*
 * Pre: Los parametros argc y argv respetan la convecion especificada en la
 * funcion "volcado".
 *
 * Post: Se obtienen las especificaciones para el volcado de los parametros
 * arc y argv.
 */
int procesarArgumentos(int argc , char *argv[] , char *modo ,
				char *operacion , char ** delimitador, int *longitud ,
				FILE **fdEntrada , FILE **fdSalida);

/*
 * Pre:-
 * Post: Imprime por pantalla el mensaje de ayuda de la aplicación.
 */
void printHelp();

/*
 * Pre:-
 * Post: Imprime por pantalla el mensaje de la version de la aplicación.
 */
void printVersion();

/*
 * Pre: Los parametros especifican las condiciones bajos las cuales se debe
 * efectuar el volcado.
 * Post: Se ha realizado el volcado.
 */
int volcar( char *modo , char ** delimitador, int *longitud ,
				FILE **fdEntrada , FILE **fdSalida);

/*
 * Pre: Los parametros especifican las condiciones bajos las cuales se debe
 * efectuar el formateo de la entrada.
 * Post: Se ha realizado el formateo de la entrada.
 */
int formatear(char ** delimitador, int *longitud ,
				FILE **fdEntrada , FILE **fdSalida);

/*
 * Pre:Se respeta la siguiente convencion de argumentos:
 Usage:

 tp0 -h\n	tp0 -V\n	tp0 [options]

 Options:
	-h, --help	Print this information.
	-V, --version	Print version and quit.
	-O, --octal	Octal dump mode: encode the input (binary file) in octal
								format (default).
	-b, --binary	Binary mode: decode the input (octal dump file) in binary
								format.
	-i, --input	Path to input file (defaults to stdin).
	-o, --output	Path to output file (defaults to stdout).
	-l, --length	Number of octal bytes per line (line length) at the
					output in octal dump mode (0 = default = no limit).
	-d, --delimiter	Delimiter between octal bytes. Could be a string, not only
					a single byte. Used as delimiter for octal output and input\n");
					files (default: none).
	Examples:
		tp0\n	tp0 -i -
		tp0 -i - -o - -O
		tp0 -b
		tp0 -i /dev/random -o /tmp/random-octaldump -l 12

* Post: Se corre el programa de acuerdo a los parametros.
*/
int main(int argc , char *argv[]) {
	return volcado(argc , argv);
}

/*
 * Pre: Se respeta la siguiente convencion de argumentos:
 *
 * Usage:

 tp0 -h\n	tp0 -V\n	tp0 [options]

 Options:
	-h, --help	Print this information.
	-V, --version	Print version and quit.
	-O, --octal	Octal dump mode: encode the input (binary file) in octal
								format (default).
	-b, --binary	Binary mode: decode the input (octal dump file) in binary
								format.
	-i, --input	Path to input file (defaults to stdin).
	-o, --output	Path to output file (defaults to stdout).
	-l, --length	Number of octal bytes per line (line length) at the
					output in octal dump mode (0 = default = no limit).
	-d, --delimiter	Delimiter between octal bytes. Could be a string, not only
					a single byte. Used as delimiter for octal output and input\n");
					files (default: none).
	Examples:
		tp0\n	tp0 -i -
		tp0 -i - -o - -O
		tp0 -b
		tp0 -i /dev/random -o /tmp/random-octaldump -l 12

 * Post: Se realiza el volcado de acuerdo a los argumentos.
 */
int volcado(int argc , char *argv[]) {
	int retorno;
	char modo; // b , o
	char operacion; // v [volcado] , h [h] , V [version]
	char *delimitador; // cadena a
	int longitud;
	FILE* fdEntrada;
	FILE* fdSalida;
	//valores por defecto
	retorno = 0;
	fdEntrada = stdin;
	fdSalida = stdout;
	longitud = 0;
	modo = 'O';
	operacion = 'v';
	delimitador = "";
	inicializarError();
	if (!procesarArgumentos(argc , argv , &modo , &operacion , &delimitador, &longitud ,
			&fdEntrada , &fdSalida)){
		retorno = 4;
	}
	else {
		switch (operacion) {
			//help
			case 'h' :	printHelp();
						break;
			//version
			case 'V' :	printVersion();
						break;
			//volcado
			case 'v' :	retorno = volcar(&modo ,&delimitador, &longitud , &fdEntrada , &fdSalida);
						break;
			//formatear
			case 'f' :  retorno = formatear(&delimitador, &longitud , &fdEntrada , &fdSalida);
						break;
		}
	}
	// Se toma desde el vector de errores el mensaje de acuerdo al indice retornado
	if (retorno != EJECUCION_CORRECTA) {
		fprintf(stderr,"\n%s\n",odump_errmsg[retorno]);
	}
	if (fdEntrada != NULL) {
		fclose(fdEntrada);
	}
	if (fdSalida != NULL) {
		fclose(fdSalida);
	}
	return retorno;
}

/*
 * Pre: Los parametros argc y argv respetan la convecion especificada en la
 * funcion "volcado".
 *
 * Post: Se obtienen las especificaciones para el volcado de los parametros
 * arc y argv.
 *
 * Returns: 1 ejecución correcta función
 * 			0 ejecución incorrecta función
 */
int procesarArgumentos(int argc , char *argv[] , char *modo , char *operacion , char ** delimitador,
						int *longitud , FILE **fdEntrada , FILE **fdSalida){
	int retorno = 1;
	if (argc > 1) {
		int numArg;
		numArg = 1;
		//Exploración de Argumentos
		while (numArg < argc) {
			char* arg;
			arg = argv[numArg];
			numArg++;
			if (strlen(arg) == 2) {
				if (arg[0] == '-') {
					switch (arg[1]) {
						//help
						case 'h' :	*operacion = 'h';
									numArg++;
									break;
						//version
						case 'V' :	*operacion = 'V';
									numArg++;
									break;
						//volcado a binario
						case 'b' :	*operacion = 'v';
									*modo = 'b';
									break;
						//volcado a Octal
						case 'O' :	*operacion = 'v';
									*modo = 'O';
									break;
						//entrada desde archivo
						case 'i' :	if (numArg < argc) {
										arg = argv[numArg];
										numArg++;
										if (*fdEntrada == stdin) {
											if (arg[0] != '-') {
												*fdEntrada = fopen(arg , "r");
												retorno |= !(*fdEntrada);
												if(*fdEntrada == NULL){
													*fdEntrada = stdin;
												}
											}
										}
									} else {
										retorno = 0;
									}
									break;
						//salida a archivo
						case 'o' :	if (numArg < argc) {
										arg = argv[numArg];
										numArg++;
										if(*fdSalida == stdout){
											if (arg[0] != '-') {
												*fdSalida = fopen(arg , "w");
												retorno |= !(*fdSalida);
												if(*fdSalida == NULL){
													*fdSalida = stdout;
												}
											}
										}
									} else {
										retorno = 0;
									}
									break;
						//delimitador
						case 'd' :	if (argv[numArg] != NULL)
										*delimitador = argv[numArg];
										numArg++;
									break;
						//longitud
						case 'l' :	*longitud = atoi(argv[numArg]);
									numArg++;
									break;
						//formatear
						case 'f' :	*operacion = 'f';
									numArg++;
									break;
						default	 :	numArg++;
									retorno = 0;
									break;
					}// fin switch
				}// fin arg[0] == '-'
				else {
					retorno = 0;
				}
			}//fin tamaño del argumento mayor o igual a 2
			else {
				retorno = 0;
			}
		}// fin while
	} // fin cantidad de argumentos mayor a 1
	return retorno;
}

/*
 * Pre:-
 * Post: Imprime por pantalla el mensaje de ayuda de la aplicacion.
 */
void printHelp() {
	printf("Usage:\n	tp0 -h\n	tp0 -V\n	tp0 [options]\n");
	printf("Options:\n");
	printf("	-h, --help	Print this information.\n");
	printf("	-V, --version	Print version and quit.\n");
	printf("	-O, --octal	Octal dump mode: encode the input (binary file) in octal \n");
	printf("			format (default).\n");
	printf("	-b, --binary	Binary mode: decode the input (octal dump file) in binary \n");
	printf("			format.\n");
	printf("	-i, --input	Path to input file (defaults to stdin).\n");
	printf("	-o, --output	Path to output file (defaults to stdout).\n");
	printf("	-l, --length	Number of octal bytes per line (line length) at the \n");
	printf("			output in octal dump mode (0 = default = no limit).\n");
	printf("	-d, --delimiter	Delimiter between octal bytes. Could be a string, not only\n");
	printf("			a single byte. Used as delimiter for octal output and input\n");
	printf("			files (default: none).\n");
	printf("Examples:\n	tp0\n	tp0 -i -\n	tp0 -i - -o - -O\n	tp0 -b\n");
	printf("	tp0 -i /dev/random -o /tmp/random-octaldump -l 12\n");
}

/*
 * Pre:-
 * Post: Imprime por pantalla el mensaje de la version de la aplicacion.
 */
void printVersion() {
	printf("Version: %s\n",VERSION);
}

/*
 * Pre: Los parametros especifican las condiciones bajos las cuales se debe
 * efectuar el volcado.
 * Post: Se ha realizado el volcado.
 *
 * Returns: 1 ejecución correcta función
 * 			0 ejecución incorrecta función
 */
int volcar( char *modo , char **delimitador , int *longitud ,
				FILE** fdEntrada , FILE** fdSalida) {
	int retorno;
	retorno = 0;
	odump_opts opts;
	opts.delim = *delimitador;
	opts.length = *longitud;
	if ( *modo == 'b' ) {
		retorno = odump_decode(fileno(*fdEntrada),fileno(*fdSalida),&opts);
	} else{
		retorno = odump_encode(fileno(*fdEntrada),fileno(*fdSalida),&opts);
	}
	return retorno;
}

/*
 * Pre: Los parametros especifican las condiciones bajos las cuales se debe
 * efectuar el formateo de la entrada.
 * Post: Se ha realizado el formateo de la entrada.
 */
int formatear(char ** delimitador, int *longitud ,
				FILE **fdEntrada , FILE **fdSalida){
	char vector[3];
	int contador;
	contador = 1;
	fscanf(*fdEntrada,"%c%c%c",&vector[0],&vector[1],&vector[2]);
	while (!feof(*fdEntrada)){
		fprintf(*fdSalida,"%c%c%c%s",vector[0],vector[1],vector[2],*delimitador);
		if ((contador == *longitud)&&(!feof(*fdEntrada))){
			fprintf(*fdSalida,"\n");
			contador = 0;
		}
		fscanf(*fdEntrada,"%c%c%c",&vector[0],&vector[1],&vector[2]);
		contador++;
	}
	return 0;
}
